/* 逻辑分析仪底层驱动示例代码

   此示例代码属于公共领域（或根据您的选择采用CC0许可）

   除非适用法律要求或以书面形式同意，本软件按"原样"分发，不附带任何明示或暗示的担保或条件。
*/

#include <stdio.h>
#include <string.h>
#include "soc/system_reg.h"     // 系统寄存器定义
#include "soc/lcd_cam_struct.h" // LCD/CAM外设结构体定义
#include "soc/lcd_cam_reg.h"    // LCD/CAM寄存器定义
#include "soc/gdma_struct.h"    // GDMA结构体定义
#include "soc/gdma_periph.h"    // GDMA外设定义
#include "soc/gdma_reg.h"       // GDMA寄存器定义
#include "esp_rom_gpio.h"       // ESP ROM GPIO函数
#include "esp_log.h"            // ESP日志功能

#include "soc/gpio_sig_map.h"                                                   // GPIO信号映射
#include "soc/gpio_periph.h"                                                    // GPIO外设定义
#include "soc/io_mux_reg.h"                                                     // IO复用寄存器
#define gpio_matrix_in(a, b, c) esp_rom_gpio_connect_in_signal(a, b, c)         // 定义GPIO输入矩阵连接宏
#define gpio_matrix_out(a, b, c, d) esp_rom_gpio_connect_out_signal(a, b, c, d) // 定义GPIO输出矩阵连接宏

// GDMA通道组定义
#if !defined(SOC_GDMA_PAIRS_PER_GROUP) && defined(SOC_GDMA_PAIRS_PER_GROUP_MAX)
#define SOC_GDMA_PAIRS_PER_GROUP SOC_GDMA_PAIRS_PER_GROUP_MAX
#endif

// 如果配置使用LEDC定时器作为PCLK
#ifdef CONFIG_ANALYZER_USE_LEDC_TIMER_FOR_PCLK
#include "driver/ledc.h" // LEDC PWM驱动
#endif
// 从DMA控制EOF的宏（当前未定义）
// #define EOF_CTRL 1

#define TAG "esp32s3_ll" // 日志标签

#include "logic_analyzer_ll.h" // 逻辑分析仪底层驱动头文件

// 如果定义为独立模式逻辑分析仪 - 将引脚定义为GPIO输入
// 否则 - 自诊断分析仪 - 根据固件定义引脚+CAM输入
#ifdef CONFIG_ANALYZER_SEPARATE_MODE
#define SEPARATE_MODE_LOGIC_ANALIZER // 定义独立模式
#else
#undef SEPARATE_MODE_LOGIC_ANALIZER // 取消定义独立模式
#endif

static intr_handle_t isr_handle; // 中断句柄
static int dma_num = 0;          // DMA通道号

// 触发中断处理函数 -> 开始传输
void IRAM_ATTR la_ll_trigger_isr(void *pin)
{
    gpio_matrix_in(0x38, CAM_V_SYNC_IDX, false); // 启用CAM（连接GPIO到CAM垂直同步信号）
    gpio_intr_disable((int)pin);                 // 禁用GPIO中断
}

// 传输完成中断 -> 来自DMA描述符空的EOF中断
static void IRAM_ATTR la_ll_dma_isr(void *handle)
{
    BaseType_t HPTaskAwoken = pdFALSE;
    // 获取DMA中断状态
    typeof(GDMA.channel[dma_num].in.int_st) status = GDMA.channel[dma_num].in.int_st;
    if (status.val == 0) // 无中断标志
    {
        return;
    }
    GDMA.channel[dma_num].in.int_clr.val = status.val; // 清除中断标志

#ifndef EOF_CTRL
    // 描述符空中断
    if (status.in_dscr_empty)
    {
        vTaskNotifyGiveFromISR((TaskHandle_t)handle, &HPTaskAwoken); // 通知任务
    }
#endif

#ifdef EOF_CTRL
    // EOF成功中断
    if (status.in_suc_eof)
    {
        vTaskNotifyGiveFromISR((TaskHandle_t)handle, &HPTaskAwoken); // 通知任务
    }
#endif

    if (HPTaskAwoken == pdTRUE) // 需要上下文切换
    {
        portYIELD_FROM_ISR(); // 触发任务切换
    }
}

// 如果配置使用LEDC定时器作为PCLK（用于采样率小于1MHz的情况）
#ifdef CONFIG_ANALYZER_USE_LEDC_TIMER_FOR_PCLK
// 设置LEDC PWM作为PCLK
static void logic_analyzer_ll_set_ledc_pclk(int sample_rate)
{
    // 配置LEDC PWM定时器
    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,              // 低速模式
        .timer_num = CONFIG_ANALYZER_LEDC_TIMER_NUMBER, // 定时器编号
        .duty_resolution = LEDC_TIMER_4_BIT,            // 4位占空比分辨率
        .freq_hz = sample_rate,                         // 设置输出频率
        .clk_cfg = LEDC_AUTO_CLK};                      // 自动时钟配置
    ledc_timer_config(&ledc_timer);

    // 配置LEDC PWM通道
    ledc_channel_config_t ledc_channel = {
        .speed_mode = LEDC_LOW_SPEED_MODE,              // 低速模式
        .channel = CONFIG_ANALYZER_LEDC_CHANNEL_NUMBER, // 通道编号
        .timer_sel = CONFIG_ANALYZER_LEDC_TIMER_NUMBER, // 定时器选择
        .intr_type = LEDC_INTR_DISABLE,                 // 禁用中断
        .gpio_num = CONFIG_ANALYZER_PCLK_PIN,           // GPIO引脚
        .duty = 8,                                      // 50%占空比（4位分辨率下8=50%）
        .hpoint = 0};                                   // 相位
    ledc_channel_config(&ledc_channel);
}
#endif

// 获取实际采样率（可能不等于配置的采样率）
int logic_analyzer_ll_get_sample_rate(int sample_rate)
{
    int ldiv = (LA_HW_CLK_SAMPLE_RATE / sample_rate); // 计算分频系数

#ifdef CONFIG_ANALYZER_USE_LEDC_TIMER_FOR_PCLK
    // 如果分频系数大于160（采样率<1MHz），使用LEDC实际频率
    if (ldiv > 160)
    {
        return ((int)ledc_get_freq(LEDC_LOW_SPEED_MODE, CONFIG_ANALYZER_LEDC_TIMER_NUMBER));
    }
#endif
    // 限制最大分频系数为160
    if (ldiv > 160)
    {
        ldiv = 160;
    }
    return LA_HW_CLK_SAMPLE_RATE / ldiv; // 返回实际采样率
}

// 设置CAM PCLK时钟和引脚（时钟来自CAM CLK或LEDC CLK如果时钟<1MHz）
static void logic_analyzer_ll_set_clock(int sample_rate)
{
    int ldiv = (LA_HW_CLK_SAMPLE_RATE / sample_rate); // 计算分频系数
    if (ldiv > 160)                                   // > 1MHz
    {
        ldiv = 160; // 限制最大分频
    }

    // 配置PCLK引脚为GPIO输出
    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CONFIG_ANALYZER_PCLK_PIN], PIN_FUNC_GPIO);
    gpio_set_direction(CONFIG_ANALYZER_PCLK_PIN, GPIO_MODE_OUTPUT);
    gpio_set_pull_mode(CONFIG_ANALYZER_PCLK_PIN, GPIO_FLOATING);
    gpio_matrix_out(CONFIG_ANALYZER_PCLK_PIN, CAM_CLK_IDX, false, false); // 连接GPIO到CAM时钟信号

#ifdef CONFIG_ANALYZER_USE_LEDC_TIMER_FOR_PCLK
    // 如果采样率<1MHz，使用LEDC作为PCLK，CAM时钟设为20MHz
    if ((LA_HW_CLK_SAMPLE_RATE / sample_rate) > 160)
    {
        ldiv = 8;                                     // CAM时钟分频为20MHz
        logic_analyzer_ll_set_ledc_pclk(sample_rate); // 设置LEDC PWM
    }
#endif

    // 配置PCLK引脚为输入
    PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[CONFIG_ANALYZER_PCLK_PIN]);
    gpio_matrix_in(CONFIG_ANALYZER_PCLK_PIN, CAM_PCLK_IDX, false); // 连接GPIO到CAM PCLK信号

    // 配置时钟分频器
    LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
    LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
    LCD_CAM.cam_ctrl.cam_clkm_div_num = ldiv; // 设置分频系数
    LCD_CAM.cam_ctrl.cam_clk_sel = 3;         // 选择时钟源：3=CLK160
}

// 设置CAM模式寄存器：8/16位，EOF控制来自DMA
static void logic_analyzer_ll_set_mode(int sample_rate, int channels)
{
    // 注意：LCD_CAM.cam_ctrl1.cam_rec_data_bytelen在logic_analyzer_ll_set_mode中清除数据长度

    // 清零控制寄存器
    LCD_CAM.cam_ctrl.val = 0;
    LCD_CAM.cam_ctrl1.val = 0;
    LCD_CAM.cam_rgb_yuv.val = 0; // 禁用转换器

    logic_analyzer_ll_set_clock(sample_rate); // 设置时钟

    // 配置CAM控制寄存器
    LCD_CAM.cam_ctrl.cam_stop_en = 0;            // 0: GDMA Rx FIFO满时不要停止
    LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 0; // 使用LCD_CAM时钟过滤
    LCD_CAM.cam_ctrl.cam_byte_order = 0;         // 不反转字节顺序
    LCD_CAM.cam_ctrl.cam_bit_order = 0;          // 不反转位顺序
    LCD_CAM.cam_ctrl.cam_line_int_en = 0;        // 禁用行中断

#ifdef EOF_CTRL
    LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; // EOF由LCD_CAM_CAM_REC_DATA_BYTELEN控制
#else
    LCD_CAM.cam_ctrl.cam_vs_eof_en = 1; // EOF由CAM_VSYNC控制
#endif

    // 配置CAM控制寄存器1
    LCD_CAM.cam_ctrl1.cam_line_int_num = 0;    // 行中断触发行数
    LCD_CAM.cam_ctrl1.cam_clk_inv = 0;         // 不反转输入时钟
    LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 0; // 禁用VSYNC滤波

    // 设置数据宽度（8位或16位）
    if (channels == 8)
    {
        LCD_CAM.cam_ctrl1.cam_2byte_en = 0; // 8位数据
    }
    else
    {
        LCD_CAM.cam_ctrl1.cam_2byte_en = 1; // 16位数据
    }

    // 其他信号配置
    LCD_CAM.cam_ctrl1.cam_de_inv = 0;        // 不反转DE信号
    LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;     // 不反转HSYNC信号
    LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;     // 不反转VSYNC信号
    LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0; // 控制信号为DE和VSYNC

    // 更新寄存器
    LCD_CAM.cam_ctrl.cam_update = 1;

    // 复位CAM和异步FIFO
    LCD_CAM.cam_ctrl1.cam_reset = 1;
    LCD_CAM.cam_ctrl1.cam_reset = 0;
    LCD_CAM.cam_ctrl1.cam_afifo_reset = 1;
    LCD_CAM.cam_ctrl1.cam_afifo_reset = 0;
}

// 设置CAM输入引脚和VSYNC、HSYNC、HENABLE为常量以停止传输
static void logic_analyzer_ll_set_pin(int *data_pins, int channels)
{
#ifndef SEPARATE_MODE_LOGIC_ANALIZER
    // 非独立模式：配置数据引脚
    for (int i = 0; i < channels; i++)
    {
        if (data_pins[i] < 0) // 引脚禁用
        {
            gpio_matrix_in(0x3C, CAM_DATA_IN0_IDX + i, false); // 连接到空信号
        }
        else
        {
            PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[data_pins[i]]);          // 启用输入
            gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false); // 连接引脚到信号
        }
    }
#else
    // 独立模式（外部逻辑分析仪）：配置数据引脚
    for (int i = 0; i < channels; i++)
    {
        if (data_pins[i] < 0) // 引脚禁用
        {
            gpio_matrix_in(0x3C, CAM_DATA_IN0_IDX + i, false); // 连接到空信号
        }
        else
        {
            gpio_reset_pin(data_pins[i]);                              // 复位引脚
            gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);           // 浮空模式
            gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);         // 输入模式
            gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false); // 连接引脚到信号
        }
    }
#endif

    // 配置控制信号
    gpio_matrix_in(0x3C, CAM_V_SYNC_IDX, false);   // VSYNC=0（停止传输）
    gpio_matrix_in(0x38, CAM_H_SYNC_IDX, false);   // HSYNC=1
    gpio_matrix_in(0x38, CAM_H_ENABLE_IDX, false); // HENABLE=1
}

// 初始化DMA通道
static esp_err_t logic_analyzer_ll_dma_init(void)
{
    // 查找空闲的GDMA通道
    for (int x = (SOC_GDMA_PAIRS_PER_GROUP - 1); x >= 0; x--)
    {
        if (GDMA.channel[x].in.link.addr == 0x0) // 通道空闲
        {
            dma_num = x;
            break;
        }
        if (x == 0) // 未找到可用通道
        {
            ESP_LOGE(TAG, "Can't found available GDMA channel");
            return ESP_FAIL;
        }
    }

    // 启用DMA时钟
    if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) == 0)
    {
        REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
        REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
        REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
        REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
    }

    // 清除并禁用DMA中断
    GDMA.channel[dma_num].in.int_clr.val = ~0;
    GDMA.channel[dma_num].in.int_ena.val = 0;

    // 配置DMA通道
    GDMA.channel[dma_num].in.conf0.val = 0;
    GDMA.channel[dma_num].in.conf1.val = 0;

    // 复位DMA通道
    GDMA.channel[dma_num].in.conf0.in_rst = 1;
    GDMA.channel[dma_num].in.conf0.in_rst = 0;

#ifdef LA_HW_PSRAM
    // 配置PSRAM突发传输大小
    GDMA.channel[dma_num].in.conf1.in_ext_mem_bk_size = GDMA_PSRAM_BURST >> 5;
#else
    // 启用描述符和数据突发传输
    GDMA.channel[dma_num].in.conf0.indscr_burst_en = 1;
    GDMA.channel[dma_num].in.conf0.in_data_burst_en = 1;
#endif

    // 配置DMA通道参数
    GDMA.channel[dma_num].in.conf1.in_check_owner = 0; // 不检查所有者
    GDMA.channel[dma_num].in.peri_sel.sel = 5;         // 选择CAM外设

    return ESP_OK;
}

// 配置逻辑分析仪
void logic_analyzer_ll_config(int *data_pins, int sample_rate, int channels, la_frame_t *frame)
{
    // 启用和配置CAM外设时钟
    if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0)
    {
        REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
        REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
        REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
        REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
    }

    // 配置引脚、模式和DMA
    logic_analyzer_ll_set_pin(data_pins, channels);
    logic_analyzer_ll_set_mode(sample_rate, channels);
    logic_analyzer_ll_dma_init();

    // 设置DMA描述符地址
    GDMA.channel[dma_num].in.link.addr = ((uint32_t)&(frame->dma[0])) & 0xfffff;

#ifdef EOF_CTRL
    LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = frame->fb.len - 1; // 设置接收数据长度（字节）
#else
    LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = 64; // EOF由DMA链表控制（长度可以是任意值）
#endif

    LCD_CAM.cam_ctrl.cam_update = 1; // 更新寄存器

    // 预启动配置
    GDMA.channel[dma_num].in.int_ena.in_suc_eof = 1; // 启用EOF中断
    GDMA.channel[dma_num].in.int_clr.in_suc_eof = 1; // 清除EOF中断标志

    GDMA.channel[dma_num].in.int_ena.in_dscr_empty = 1; // 启用描述符空中断
    GDMA.channel[dma_num].in.int_clr.in_dscr_empty = 1; // 清除描述符空中断标志

    GDMA.channel[dma_num].in.link.stop = 0;  // 停止位清零
    GDMA.channel[dma_num].in.link.start = 1; // 启动DMA传输
    LCD_CAM.cam_ctrl1.cam_start = 1;         // 启用CAM传输
}

// 无触发启动传输（直接设置VSYNC为启用）
void logic_analyzer_ll_start()
{
    gpio_matrix_in(0x38, CAM_V_SYNC_IDX, false); // 设置VSYNC=1（启用传输）
}

// 带触发启动传输（设置中断，在中断处理函数中启用VSYNC）
void logic_analyzer_ll_triggered_start(int pin_trigger, int trigger_edge)
{
#ifdef CONFIG_ANALYZER_USE_HI_LEVEL_INTERRUPT
    ll_hi_level_triggered_isr_start(pin_trigger, trigger_edge); // 使用高优先级中断
#else
    gpio_install_isr_service(0);                                               // 安装GPIO中断服务
    gpio_set_intr_type(pin_trigger, trigger_edge);                             // 设置中断触发类型
    gpio_isr_handler_add(pin_trigger, la_ll_trigger_isr, (void *)pin_trigger); // 添加中断处理函数
    gpio_intr_disable(pin_trigger);                                            // 禁用中断（先禁用再启用以确保正确配置）
    gpio_intr_enable(pin_trigger);                                             // 启用中断（在中断中启动传输）
#endif
}

// 完全停止CAM、DMA、中断、PCLK，复位PCLK引脚为默认状态
void logic_analyzer_ll_stop()
{
    LCD_CAM.cam_ctrl1.cam_start = 0;        // 停止CAM传输
    GDMA.channel[dma_num].in.link.stop = 1; // 停止DMA传输
    GDMA.channel[dma_num].in.link.start = 0;
    GDMA.channel[dma_num].in.link.addr = 0x0; // 清除DMA描述符地址

    // 禁用并清除中断
    GDMA.channel[dma_num].in.int_ena.in_suc_eof = 0;
    GDMA.channel[dma_num].in.int_clr.in_suc_eof = 1;
    GDMA.channel[dma_num].in.int_ena.in_dscr_empty = 0;
    GDMA.channel[dma_num].in.int_clr.in_dscr_empty = 1;

#ifdef CONFIG_ANALYZER_USE_LEDC_TIMER_FOR_PCLK
    ledc_stop(LEDC_LOW_SPEED_MODE, CONFIG_ANALYZER_LEDC_CHANNEL_NUMBER, 0); // 停止LEDC PWM
#endif

    gpio_set_direction(CONFIG_ANALYZER_PCLK_PIN, GPIO_MODE_DISABLE); // 禁用PCLK引脚
}

// 初始化DMA EOF中断
esp_err_t logic_analyzer_ll_init_dma_eof_isr(TaskHandle_t task)
{
    esp_err_t ret = ESP_OK;

    // 分配DMA中断
    ret = esp_intr_alloc(gdma_periph_signals.groups[0].pairs[dma_num].rx_irq_id,
                         ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
                         la_ll_dma_isr, (void *)task, &isr_handle);

    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "DMA interrupt allocation of analyzer failed");
        return ret;
    }
    return ret;
}

// 反初始化DMA EOF中断
void logic_analyzer_ll_deinit_dma_eof_isr()
{
    esp_intr_free(isr_handle); // 释放中断
}